package com.oystertech.sportcms.common.exception;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.oystertech.sportcms.common.result.Result;
import com.oystertech.sportcms.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import jakarta.servlet.ServletException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;

import java.sql.SQLSyntaxErrorException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 全局系统异常处理器
 * <p>
 * 调整异常处理的HTTP状态码，丰富异常处理类型
 *
 * @author Gadfly
 * @since 2020-02-25 13:54
 **/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

	@ExceptionHandler(BindException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(BindException e) {
		log.error("BindException:{}", e.getMessage());
		String msg = e.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("；"));
		return Result.failed(ResultCode.PARAM_ERROR, msg);
	}

	/**
	 * RequestParam参数的校验
	 *
	 * @param e
	 * @param <T>
	 * @return
	 */
	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(ConstraintViolationException e) {
		log.error("ConstraintViolationException:{}", e.getMessage());
		String msg = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("；"));
		return Result.failed(ResultCode.PARAM_ERROR, msg);
	}

	/**
	 * RequestBody参数的校验
	 *
	 * @param e
	 * @param <T>
	 * @return
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(MethodArgumentNotValidException e) {
		log.error("MethodArgumentNotValidException:{}", e.getMessage());
		String msg = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining("；"));
		return Result.failed(ResultCode.PARAM_ERROR, msg);
	}

	@ExceptionHandler(NoHandlerFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	public <T> Result<T> processException(NoHandlerFoundException e) {
		log.error(e.getMessage(), e);
		return Result.failed(ResultCode.RESOURCE_NOT_FOUND);
	}

	/**
	 * MissingServletRequestParameterException
	 */
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(MissingServletRequestParameterException e) {
		log.error(e.getMessage(), e);
		return Result.failed(ResultCode.PARAM_IS_NULL);
	}

	/**
	 * MethodArgumentTypeMismatchException
	 */
	@ExceptionHandler(MethodArgumentTypeMismatchException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(MethodArgumentTypeMismatchException e) {
		log.error(e.getMessage(), e);
		return Result.failed(ResultCode.PARAM_ERROR, "类型错误");
	}

	/**
	 * ServletException
	 */
	@ExceptionHandler(ServletException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(ServletException e) {
		log.error(e.getMessage(), e);
		return Result.failed(e.getMessage());
	}

	@ExceptionHandler(IllegalArgumentException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> handleIllegalArgumentException(IllegalArgumentException e) {
		log.error("非法参数异常，异常原因：{}", e.getMessage(), e);
		return Result.failed(e.getMessage());
	}

	@ExceptionHandler(JsonProcessingException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> handleJsonProcessingException(JsonProcessingException e) {
		log.error("Json转换异常，异常原因：{}", e.getMessage(), e);
		return Result.failed(e.getMessage());
	}

	/**
	 * HttpMessageNotReadableException
	 */
	@ExceptionHandler(HttpMessageNotReadableException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(HttpMessageNotReadableException e) {
		log.error(e.getMessage(), e);
		String errorMessage = "请求体不可为空";
		Throwable cause = e.getCause();
		if (cause != null) {
			errorMessage = convertMessage(cause);
		}
		return Result.failed(errorMessage);
	}

	@ExceptionHandler(TypeMismatchException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> processException(TypeMismatchException e) {
		log.error(e.getMessage(), e);
		return Result.failed(e.getMessage());
	}

	@ExceptionHandler(BadSqlGrammarException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public <T> Result<T> handleBadSqlGrammarException(BadSqlGrammarException e) {
		log.error(e.getMessage(), e);
		return Result.failed("500001", e.getMessage());
	}

	@ExceptionHandler(SQLSyntaxErrorException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public <T> Result<T> processSQLSyntaxErrorException(SQLSyntaxErrorException e) {
		log.error(e.getMessage(), e);
		return Result.failed("500002", e.getMessage());
	}


	@ExceptionHandler(BusinessException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public <T> Result<T> handleBizException(BusinessException e) {
		log.error("biz exception: {}", e.getMessage());
		if (e.getResultCode() != null) {
			return Result.failed(e.getResultCode());
		}
		return Result.failed(e.getMessage());
	}

	@ExceptionHandler(Exception.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public <T> Result<String> handleException(Exception e) {
		log.error("unknown exception: {}", e.getMessage());
		return Result.failed("500", "未知错误", e.getLocalizedMessage());
	}

	/**
	 * 传参类型错误时，用于消息转换
	 *
	 * @param throwable 异常
	 * @return 错误信息
	 */
	private String convertMessage(Throwable throwable) {
		String error = throwable.toString();
		String regulation = "\\[\"(.*?)\"]+";
		Pattern pattern = Pattern.compile(regulation);
		Matcher matcher = pattern.matcher(error);
		String group = "";
		if (matcher.find()) {
			String matchString = matcher.group();
			matchString = matchString.replace("[", "").replace("]", "");
			matchString = "%s字段类型错误".formatted(matchString.replaceAll("\\\"", ""));
			group += matchString;
		}
		return group;
	}
}
